' Copyright (C) 2010 depenga.com
' This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
' This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
' You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.

' Usage:
'cscript qtpRunner.vbs

Option Explicit

Dim qtp : Set qtp = New QtpRunner
qtp.runTest()

''c* depenga.com/QtpRunner
' NAME
'	QtpRunner
' FUNCTION
'    This class represents Qtp application
' SYNOPSIS
'    Set qtp = New QtpRunner
''
Class QtpRunner
	Private qtpApp
	''s* QtpRunner/settings
	' NAME
	'	settings
	' FUNCTION
	'	dictionary containing information about particular settings stored in config.properties 
	''		
	Private settings
	
	''m* QtpRunner/Class_Initialize
	' NAME
	'	Class_Initialize
	' FUNCTION
	'	Constructor of QtpRunner class
	' ARGUMENTS 
	'	As any VBS constructor it does not take any arguments 
	''	
   	Private Sub Class_Initialize
		Set qtpApp = CreateObject("QuickTest.Application")
		readConfiguration()
	End Sub

	Private Sub readConfiguration
		Dim settingsMng : Set settingsMng = New SettingsManager
		Set settings = settingsMng.getSettings("config.properties")
		dim key
		'For each key in settings
		'	WScript.Echo key & " " & settings(key)
		'Next
	End Sub
	
	''m* QtpRunner/runTest
	' NAME
	'	runTest
	' FUNCTION
	'	Method that runs QTP test specified in config.properties file. 
	'	Runs the test and waits for test to finish.
	' TODO
	'	set default settings if none exist in config.properties file ie. set ViewResults to False etc.
	''		
	Public Sub runTest
		qtpApp.Launch
		If settings.Exists("Visible") Then
			qtpApp.Visible = settings.Item("Visible")		
		Else
			qtpApp.Visible = True
		End If	
		
		If settings.Exists("testPath") Then
			qtpApp.Open settings.Item("testPath"), True	'open test in read-only mode		
		Else
			qtpApp.Open ".\", True
		End If					
		
		If settings.Exists("ViewResults") Then
			qtpApp.Options.Run.ViewResults = settings.Item("ViewResults")		
		Else
			qtpApp.Options.Run.ViewResults = False
		End If
		
		Dim qtpResultsOptions : Set qtpResultsOptions = CreateObject("QuickTest.RunResultsOptions")
		If settings.Exists("ResultsLocation") Then
			qtpResultsOptions.ResultsLocation = settings.Item("ResultsLocation")		
		'Else
			'qtpResultsOptions.ResultsLocation = ""
		End If		

		If settings.Exists("ImageCaptureForTestResults") Then
			qtpApp.Options.Run.ImageCaptureForTestResults = settings.Item("ImageCaptureForTestResults")		
		End If				
		
		If settings.Exists("RunMode") Then
			qtpApp.Options.Run.RunMode = settings.Item("RunMode")		
		End If				
		
		If settings.Exists("IterationMode") Then
			qtpApp.Test.Settings.Run.IterationMode = settings.Item("IterationMode")		
		End If

		If settings.Exists("StartIteration") Then
			qtpApp.Test.Settings.Run.StartIteration = settings.Item("StartIteration")		
		End If		
		
		If settings.Exists("EndIteration") Then
			qtpApp.Test.Settings.Run.EndIteration = settings.Item("EndIteration")		
		End If	

		If settings.Exists("OnError") Then
			qtpApp.Test.Settings.Run.OnError = settings.Item("OnError")		
		End If			
		
		qtpApp.Test.Run qtpResultsOptions
		While qtpApp.Test.IsRunning
			'Wait For Test To Finish
		Wend
		qtpApp.Test.Close
		qtpApp.Quit
		Set qtpApp = Nothing
	End Sub
End Class


''c* depenga.com/SettingsManager
' NAME
'	SettingsManager
' FUNCTION
'    Class that manages QTP settings, reads them from file and command line. Contains methods for reading specified properties and saving them in structure called settings.
''
Class SettingsManager
	Private settings
	
	''m* QtpRunner/getSettings
	' NAME
	'	getSettings
	' INPUTS
	'	filePath (optional) - path to config file, default value: "config.properties"
	' RETURN
	'	settings
	' FUNCTION
	'	Method that returns settings that are passed as command line arguments and saved in a configuration file. Command line arguments have higher priority that config file settings
	'	i.e. if the property is saved in config file and passed as cmd line argument then command line arg is used prior to the value saved in configuration file
	''
	Public Function getSettings(filePath)
		Dim propertyFile : Set propertyFile = New PropertyFileReader
		If filePath<>"" Then
			propertyFile.setFilePath = filePath
		Else ' if file path is empty then use "config.properties" located in the same direcory as the script
			propertyFile.setFilePath = "config.properties"
		End If

		Dim cmdArg : Set cmdArg = New CmdArgumentsReader
		' merge settings that are passed in command line with these from config file
		Set settings = mergeSettings(cmdArg.getSettings, propertyFile.getSettings())
		' return settings list
		Set getSettings = settings
	End Function
	
	' assumption: first argument is more important than the second one
	Private Function mergeSettings(settings1, settings2)
		' If the first dictionary has more elements than the second one then treat it as the main one. 
		If settings1.Count > settings2.Count Then
			Dim key
			' Check if the 1st dic has duplicated key (i.e. if the key is already present in the 2nd dic);
			' if no then add new key, value pair to 1st dic, otherwise do nothing
			For each key in settings2
				If not settings1.Exists(key) Then
					settings1.Add key, settings2(key)
				End If
			Next
			' return 1st dictionary with additional values
			Set mergeSettings = settings1
		' Otherwise treat 2nd one as the main dictionary
		Else
			Dim key1
			For each key1 in settings1
				' Check if the 2nd dic has duplicated key (i.e. if the key is already present in the 2nd dic);
				' if yes then replace value,if no then add new key, value pair to 2nd dic
				If settings2.Exists(key1) Then
					settings2(key1) = settings1(key1)
				Else
					settings2.Add key1, settings1(key1)
				End If
			Next
			Set mergeSettings = settings2
		End If
	End Function	
End Class

'Dim prop : Set prop  = New PropertyFile
'prop.readFile("config.properties")

''c* depenga.com/PropertyFileReader
' NAME
'	PropertyFileReader
' FUNCTION
'    Class represents config.properties file. Contains methods for reading specified properties and saving them in structure called settings.
''
Class PropertyFileReader
	Private filePath
	Private settings
	
	Public Property Let setFilePath(fPath)
		filePath = fPath
	End Property
	
	Public Property Get getSettings
		readFile(filePath)
		Set getSettings = settings
	End Property
	
	Public Sub readFile(filePath)
		Set settings = CreateObject( "Scripting.Dictionary" )
		Dim configFile : Set configFile = CreateObject("Scripting.FileSystemObject")
		If configFile.FileExists(filePath) Then
			Dim fileHandler : Set fileHandler = configFile.OpenTextFile(filePath, ForReading)
			While not fileHandler.AtEndOfStream
				Dim line : line = Trim( fileHandler.ReadLine )	'remove white chars
				If line <> "" Then	'check if line is not empty
					If Left(line, 1)<>"#" Then	'if line is not a comment then
						Dim div : div = Split( line, "=" )	'split line to (key, value) pair
						If UBound(div) = 1 Then	'if the line is correct key value pair (does contain only one '=' char) then
							settings.Add Trim(div(0)), Trim(div(1))
						End If	
					End If
				End If
			Wend
			fileHandler.Close
		'Else
			' WScript.Echo "File " & filePath & " does not exists; using command line arguments only."
			'@TODO save error messages to error.log file
		End If
	End Sub
End Class

''c* depenga.com/CmdArgumentsReader
' NAME
'	CmdArgumentsReader
' FUNCTION
'    Class represents received arguments list from command prompt as parameters passed to vbs script. Contains methods for reading specified properties and saving them in structure called settings.
''
Class CmdArgumentsReader
	Private settings
	
	Public Property Get getSettings
		readArgs()
		Set getSettings = settings
	End Property
	
	Public Sub readArgs
		Set settings = CreateObject( "Scripting.Dictionary" )
		
		Dim args : Set args = WScript.Arguments

		Dim i
		For i = 0 to args.Count-1
			Dim div : div = Split(args(i), "=")
			If (Ubound(div) = 1) Then
			' double if is required, otherwise scripthost will evaluate everything and throw "Subscript out of range: '[number: 1]'"
				If (div(0)<>"") And (div(1)<>"") Then
					settings.Add div(0), div(1)
				End If
			End If
		Next
	End Sub
End Class

